<template>
  <div
    ref="sideGraphPanel"
    :class="{
      'comp-side-graph-panel': true,
      'guest-mode': isVisitor || lockMode,
    }"
    @click.stop="handleClick"
  >
    <a-tabs
      v-model="activeTab"
      class="comp-side-graph-panel-tabs"
      size="small"
      :animated="false"
      default-active-key="systemComponent"
      @change="onTabChange"
    >
      <!-- 我的组件part -->
      <a-tab-pane key="myComponent" tab="我的组件" class="my-component">
        <div v-if="!myComponentLoadedFlag" class="spin-box">
          <a-spin class="spin"></a-spin>
        </div>
        <div v-else>
          <div class="my-component-list">
            <div class="search-box">
              <a-input
                v-model="myComponentSearchWords"
                placeholder="搜索内容"
                allow-clear
                @change="onMySearchWordsChange"
              />
            </div>
            <a-collapse
              v-model="myComponentCollapseActiveKeys.total"
              expand-icon-position="left"
              @change="onMyCollapseChange"
            >
              <a-collapse-panel
                v-for="widgetMenuKey in Object.keys(myComponentTargetWidget)"
                :key="widgetMenuKey"
              >
                <template slot="header">
                  <div
                    class="my-component-list-title my-component-list-header-title"
                  >
                    {{ menuKeyMapping[widgetMenuKey] }}
                  </div>
                </template>
                <div
                  v-if="myComponentTargetWidget[widgetMenuKey].length === 0"
                  class="empty-info"
                >
                  暂无组件
                </div>
                <div v-else class="widget-menu-item-div">
                  <a-collapse
                    v-model="myComponentCollapseActiveKeys[widgetMenuKey]"
                    expand-icon-position="left"
                  >
                    <a-collapse-panel
                      v-for="taskWidget in myComponentTargetWidget[
                        widgetMenuKey
                      ]"
                      :key="`${taskWidget.tid}`"
                      :class="`pipeline-${taskWidget.tid}`"
                    >
                      <template slot="header">
                        <div class="my-component-list-title">
                          {{ taskWidget.taskName }}
                        </div>
                      </template>
                      <div class="widget-item-list-div">
                        <!-- widgets 列表 -->
                        <a-list
                          :grid="{
                            column: widgetMenuKey === 'gis' ? 1 : 2,
                            gutter: 10,
                          }"
                          :data-source="taskWidget.widgets"
                        >
                          <a-list-item
                            slot="renderItem"
                            slot-scope="widget, widgetIndex"
                          >
                            <div
                              :key="`${widget.type}-${widget.tid}-${widgetIndex}`"
                              class="widget-item-div"
                              style="position: relative;"
                              draggable="true"
                              @mouseenter.stop="doCrossHighlighting(widget)"
                              @mouseleave.stop="cancelCrossHighlighting"
                              @mousedown.stop="onMyMouseDown"
                              @dragstart="
                                (event) => {
                                  onMyDragstart(event, widget, widgetMenuKey)
                                }
                              "
                            >
                              <template>
                                <img
                                  alt=""
                                  :src="`${publicPath}${widget.imgSrc}`"
                                  :style="{
                                    height: '46.50px',
                                    width: 'auto',
                                  }"
                                />
                              </template>
                              <div class="hover-visible">
                                <div class="hover-content">
                                  {{ widget.name }}
                                </div>
                                <!--删除icon-->
                                <div
                                  v-if="
                                    (widget.id !== null &&
                                      !isVisitor &&
                                      !lockMode) ||
                                    widgetMenuKey === 'gis'
                                  "
                                  class="delete-icon"
                                >
                                  <a-tooltip placement="top">
                                    <template slot="title">
                                      <span>删除</span>
                                    </template>
                                    <a-icon-font
                                      type="iconicon-beifen5-copy"
                                      class="little-icon"
                                      @click="onDelete(widget)"
                                    />
                                  </a-tooltip>
                                </div>
                                <!--编辑icon-->
                                <div
                                  v-if="
                                    (widget.id !== null &&
                                      !isVisitor &&
                                      !lockMode) ||
                                    widgetMenuKey === 'gis'
                                  "
                                  class="edit-icon"
                                >
                                  <a-tooltip placement="top">
                                    <template slot="title">
                                      <span>编辑</span>
                                    </template>
                                    <a-icon-font
                                      type="iconicon-beifen6-copy"
                                      class="little-icon"
                                      @click="
                                        editMyVisualization(
                                          widget,
                                          widgetMenuKey
                                        )
                                      "
                                    />
                                  </a-tooltip>
                                </div>
                              </div>
                            </div>
                          </a-list-item>
                        </a-list>
                        <div
                          v-if="
                            !isVisitor && !lockMode && widgetMenuKey !== 'gis'
                          "
                          class="widget-item-list-add-btn"
                          @click="addMyVisualization(taskWidget, widgetMenuKey)"
                        >
                          <div></div>
                          <a-icon type="plus-circle" />
                          <span> 添加可视化 </span>
                        </div>
                      </div>
                    </a-collapse-panel>
                  </a-collapse>
                </div>
              </a-collapse-panel>
            </a-collapse>
          </div>
        </div>
      </a-tab-pane>
      <!-- 系统组件part -->
      <a-tab-pane key="systemComponent" tab="系统组件">
        <div class="search-box">
          <a-input
            v-model="systemComponentSearchWords"
            placeholder="搜索内容"
            allow-clear
          />
        </div>
        <div
          class="tab-box"
          :style="
            isVisitor || lockMode
              ? 'cursor: not-allowed; opacity: 0.45;'
              : 'cursor: pointer'
          "
        >
          <a-tabs
            v-show="systemComponentSearchWords === ''"
            tab-position="left"
            :active-key="systemComponentSelectedType"
            @change="onSystemSelectedTypeChange($event, true)"
          >
            <a-tab-pane v-for="item in Object.keys(chartTypes)" :key="item">
              <span slot="tab">
                <a-tooltip placement="left">
                  <template slot="title">
                    <span>{{ chartTypes[item].title }}</span>
                  </template>
                  <img
                    v-if="chartTypes[item].active"
                    :class="
                      chartTypes[item].iconClass
                        ? chartTypes[item].iconClass
                        : ''
                    "
                    draggable="false"
                    :src="
                      publicPath + chartTypes[item].iconCharCode + '-active.svg'
                    "
                    alt=""
                    :style="{ width: '22px' }"
                  />
                  <img
                    v-else
                    :class="
                      chartTypes[item].iconClass
                        ? chartTypes[item].iconClass
                        : ''
                    "
                    draggable="false"
                    :src="publicPath + chartTypes[item].iconCharCode + '.svg'"
                    alt=""
                    :style="{ width: '22px' }"
                  />
                </a-tooltip>
              </span>
              <div
                class="chart-item-container"
                :style="{ '--after-height': `${itemContainerAfterHeight}px` }"
              >
                <div
                  v-for="(chartItem, j) in systemComponents"
                  :key="`chart-${j}`"
                  :class="
                    ['divider'].includes(chartItem.chartType)
                      ? 'divider'
                      : 'chart-item'
                  "
                  draggable="true"
                  @mousedown.stop="onSystemMouseDown"
                  @dragstart="
                    (event) => {
                      onSystemDragstart(
                        event,
                        chartItem.chartType,
                        chartItem.name
                      )
                    }
                  "
                >
                  <span v-if="['divider'].includes(chartItem.chartType)">{{
                    chartItem.title
                  }}</span>
                  <card v-else size="small" style="pointer-events: none;">
                    <span
                      slot="title"
                      :style="{ 'font-weight': 600, color: '#5d637e' }"
                    >
                      {{ chartItem.title }}
                    </span>
                    <img
                      alt=""
                      :src="`${publicPath}${chartItem.imgSrc}`"
                      :style="{ height: '100%', width: '100%' }"
                    />
                  </card>
                </div>
              </div>
            </a-tab-pane>
          </a-tabs>
          <div
            v-if="systemComponentSearchWords !== ''"
            class="chart-item-container-search"
          >
            <div>
              <div
                v-for="(chartItem, j) in systemComponents"
                :key="`chart-${j}`"
                class="chart-item-search"
                draggable="true"
                @mousedown.stop="onSystemMouseDown"
                @dragstart="
                  (event) => {
                    onSystemDragstart(
                      event,
                      chartItem.chartType,
                      chartItem.name
                    )
                  }
                "
              >
                <card size="small" style="pointer-events: none;">
                  <span
                    slot="title"
                    :style="{ 'font-weight': 600, color: '#5d637e' }"
                  >
                    {{ chartItem.title }}
                  </span>
                  <img
                    alt=""
                    :src="`${publicPath}${chartItem.imgSrc}`"
                    :style="{ height: '100%', width: '100%' }"
                  />
                </card>
              </div>
            </div>
          </div>
        </div>
      </a-tab-pane>
    </a-tabs>
    <!-- 添加，编辑可视化modal -->
    <modal-visualization
      :chart-config="myModalVisualizationConfig.chartConfig"
      :dataset-list="myModalVisualizationConfig.datasetList"
      :visible="myModalVisualizationConfig.visible"
      @cancel="myModalVisualizationConfig.visible = false"
      @ok="onSave"
    >
    </modal-visualization>
  </div>
</template>
<script lang="ts">
import { Card } from 'ant-design-vue'
import { Vue, Component, Watch, Prop } from 'vue-property-decorator'
import {
  chartMaps,
  chartTypes,
  chartList,
  displayChartMaps,
} from '@/config/contant'

import {
  widgetQueryByProject,
  deleteWidget,
  saveWidget,
  updateWidget,
} from '@/api/widget'
import ModalVisualization from '@/components/common/VisualizationDashboard/ModalVisualization.vue'
import Chart from '@/components/chart/chart.vue'

import VisualizationStore from '@/store/modules/visualization'
import DataViewStore from '@/store/modules/dataview'
import UserStore from '@/store/modules/user'
import { Widget } from '@/util/chart-widget'
import { publicPath } from '@/api/constants'
import GisMapPublic from '@/components/gis/public/GisMapPublic.vue'
import { setLocalStorage } from '@/util/util'
import { throttle } from 'lodash'

@Component({
  components: {
    Card,
    Chart,
    ModalVisualization,
    GisMapPublic,
  },
})
export default class SideGraphPanel extends Vue {
  @Prop({ type: Boolean, default: true }) private lockMode!: boolean
  private mouseDownPos: any = {
    x: 0,
    y: 0,
  }
  //  系统组件搜索的关键词
  systemComponentSearchWords: string = ''
  systemComponentSelectedType: string = 'barChart'
  private chartMaps: any = chartMaps
  private chartList: any = chartList
  private chartTypes: any = chartTypes
  private displayChartMaps: any = displayChartMaps
  private publicPath = publicPath // 基础URL
  // 各种图表大类包含子类型缩略图的分布位置
  chartTypePosList: any = []
  // 所有图表子类型的数量
  chartItemNumber: any = 0
  // 图表类型图库中当前滚动条的位置
  scrollInChartTabTopValue: any = 0
  // 图表类型图库下方需要插入空白元素的高度
  itemContainerAfterHeight: any = 0
  // 监听页面尺寸变化时的节流函数
  throttleOnResize: any = throttle(this.setItemContainerAfterHeight, 100)
  // 图标类型图库中各类型分割距离，改动需要协同style
  private DIVIDER_HEIGHT = 40
  private CHART_ITEM_SIBLING_MARGIN = 10
  myComponentLoadedFlag: boolean = false
  myComponentWidget: {
    dataset: Array<any>
    pipeline: Array<any>
    gis: Array<any>
  } = {
    dataset: [],
    pipeline: [],
    gis: [],
  } // 原始的层次化的 dataset/pipeline->tasks->widgets
  myComponentWidgetList: Array<any> = [] // 摊平的widgets 存到VisualizationStore
  //  我的组件搜索的关键词
  myComponentSearchWords: string = ''
  myComponentWidgetSearchResult: {
    dataset: Array<any>
    pipeline: Array<any>
  } = {
    dataset: [],
    pipeline: [],
  }
  //  激活的tab 名称
  activeTab: any = 'systemComponent'
  // 缓存collapse展开状态
  myComponentCollapseActiveKeys: any = {
    total: ['dataset', 'pipeline', 'gis'],
    dataset: [],
    pipeline: [],
  }
  myComponentCollapseActiveKeysCache: any = null
  //  modal所需的可视化参数
  myModalVisualizationConfig: {
    visible: boolean
    chartConfig: any
    datasetList: {
      dataset: Array<any>
      pipeline: Array<any>
    }
    widgetId?: number
  } = {
    visible: false,
    chartConfig: {},
    datasetList: {
      dataset: [],
      pipeline: [],
    },
  }

  private menuKeyMapping: any = {
    // 中文mapping
    dataset: '数据集',
    pipeline: 'Pipeline',
    gis: '时空分析',
  }

  public get isVisitor() {
    //  访客身份
    return UserStore.isProjectGuest
  }

  /**
   * pipeline预览视图中，选中的节点
   */
  public get selectedNode() {
    return DataViewStore.preSelectNode
  }
  /**
   * 根据pipeline中选中的节点，定位到tab 对应的位置
   */
  @Watch('selectedNode')
  private handleSelectNode(newValue: any) {
    if (newValue) {
      this.activeTab = 'myComponent'
      this.onTabChange('myComponent')
      this.myComponentCollapseActiveKeys.pipeline = String(newValue.id)
      this.myComponentCollapseActiveKeys.dataset = []
      setTimeout(() => {
        ;(
          document.querySelector(`.pipeline-${newValue.id}`) as HTMLElement
        ).scrollIntoView()
        ;(
          document.querySelector('.ant-tabs-nav-wrap') as HTMLElement
        ).scrollIntoView()
      }, 500)
    }
  }

  public get systemComponents() {
    const result: any[] = []
    const keywords = this.systemComponentSearchWords.trim()
    this.chartList.forEach((k: any) => {
      if (
        keywords === '' ||
        (keywords !== '' &&
          k.chartType !== 'divider' &&
          (k.title.includes(keywords) ||
            k.name.toLowerCase().includes(keywords.toLowerCase())))
      ) {
        result.push(k)
      }
    })
    return result.filter((k) => k.inSystem !== false) // 排除一些demo的类型
  }

  get myComponentTargetWidget() {
    return this.myComponentSearchWords.trim()
      ? this.myComponentWidgetSearchResult
      : this.myComponentWidget
  }

  /**
   * 关闭预览pipeline中的节点详情显示
   */
  public handleClick() {
    VisualizationStore.changePreviewPopStatus('close')
  }

  mounted() {
    Object.keys(chartTypes).forEach((chartType: string) => {
      chartTypes[chartType].active = false
    })
    this.setChartTypePosList()
    this.onSystemSelectedTypeChange('barChart', true)
    // 监听滚动条位置，动态更新左侧图表大类Tab的状态
    const tabsContent = this.$el.querySelector(
      '.ant-tabs .ant-tabs-left-content'
    ) as HTMLElement
    tabsContent.scrollTop = 0
    tabsContent.addEventListener('scroll', this.handScrollInChartTab)
    // 在组件中添加一段空白元素，使得切换图表大类时可以使列表滚动位置准确定位到对应图表类型
    this.setItemContainerAfterHeight()
    window.addEventListener('resize', this.throttleOnResize)
  }

  beforeDestroy() {
    window.removeEventListener('resize', this.throttleOnResize)
  }

  /**
   * 确定展示组件中每种图表类型所对应图表缩略图的起止位置
   */
  setChartTypePosList() {
    let lastTypeEnd: number = 0
    const chartMapsKeys = Object.keys(displayChartMaps)
    const chartMapsValues = Object.values(displayChartMaps)
    chartMapsKeys.forEach((key, index) => {
      this.chartTypePosList.push({
        name: key,
        start: lastTypeEnd,
        end: lastTypeEnd + chartMapsValues[index].length,
        length: chartMapsValues[index].length,
        tabIndex: index,
      })
      lastTypeEnd += chartMapsValues[index].length
    })
    this.chartItemNumber = lastTypeEnd
  }

  /**
   * 根据当前状态下各个页面元素的尺寸，动态计算需要插入空白元素的尺寸（高度）
   */
  setItemContainerAfterHeight() {
    const tabsContent = this.$el.querySelector(
      '.ant-tabs .ant-tabs-left-content'
    ) as HTMLElement
    const tabsHeight = (
      this.$el.querySelector(
        '.ant-tabs-content.ant-tabs-content-animated.ant-tabs-left-content'
      ) as HTMLElement
    ).offsetHeight
    const chartItemHeight =
      (this.$el.querySelectorAll('.chart-item')[1] as HTMLElement).offsetTop -
      this.DIVIDER_HEIGHT
    const lastTypeItemNumber = this.chartTypePosList.slice(-1)[0].length
    this.itemContainerAfterHeight = Math.max(
      0,
      tabsHeight -
        (chartItemHeight * lastTypeItemNumber +
          this.DIVIDER_HEIGHT -
          this.CHART_ITEM_SIBLING_MARGIN)
    )
    tabsContent.scrollTop = Math.min(
      tabsContent.scrollTop,
      this.chartItemNumber * chartItemHeight +
        this.itemContainerAfterHeight -
        tabsHeight -
        this.CHART_ITEM_SIBLING_MARGIN
    )
  }

  /**
   * 处理图表类型列表组件中的滚动条事件
   */
  handScrollInChartTab() {
    const tabsContent = this.$el.querySelector(
      '.ant-tabs .ant-tabs-left-content'
    ) as HTMLElement
    const chartItemHeight =
      (this.$el.querySelectorAll('.chart-item')[1] as HTMLElement).offsetTop -
      this.DIVIDER_HEIGHT
    const cardHeadHeight = (
      this.$el.querySelectorAll('.ant-card-head')[1] as HTMLElement
    ).offsetHeight
    // 与onSystemSelectedTypeChange中校准margin类似，此处计算activeTabPos也需要校准margin
    // 判断当前scrollTop是否进入某个类别的临界起点
    let activeTabPos: number =
      this.chartTypePosList.length -
      1 -
      [...this.chartTypePosList]
        .reverse()
        .findIndex(
          (itemPos: any) =>
            itemPos.start * chartItemHeight +
              this.DIVIDER_HEIGHT * itemPos.tabIndex -
              this.CHART_ITEM_SIBLING_MARGIN * itemPos.tabIndex <=
            tabsContent.scrollTop
        )
    // 判断当前scrollTop是否到达某个类别的临界终点，终点设定为：超过当前tab所属最后一个chart的标题
    if (
      tabsContent.scrollTop >=
      (this.chartTypePosList[activeTabPos].end - 1) * chartItemHeight +
        cardHeadHeight +
        (activeTabPos + 1) * this.DIVIDER_HEIGHT -
        activeTabPos * this.CHART_ITEM_SIBLING_MARGIN
    ) {
      activeTabPos += 1
    }
    this.scrollInChartTabTopValue = tabsContent.scrollTop
    this.onSystemSelectedTypeChange(
      this.chartTypePosList[activeTabPos].name,
      false
    )
  }

  /**
   * 系统组件 chart 鼠标down事件
   */
  onSystemMouseDown(event: any) {
    this.mouseDownPos.x = event.layerX - 6
    this.mouseDownPos.y = event.layerY - event.target.offsetTop
    // 这里用来把offset 先存储起来，方便drop时定位
    VisualizationStore.setDraggingOffset(this.mouseDownPos)
  }

  /**
   * 系统组件中 - 拖拽组件
   */
  onSystemDragstart(event: any, chartType: string, chartSubType: string) {
    // console.log(`${chartType}|${chartSubType}`)
    if (this.isVisitor || this.lockMode) {
      return
    }
    event.dataTransfer.setData('text', `${chartType}|${chartSubType}`)
  }
  /**
   * 切换系统组件中的ChartType
   */
  onSystemSelectedTypeChange(item: any, updateScroll: boolean) {
    this.systemComponentSelectedType = item
    Object.keys(chartTypes).forEach((key) => {
      chartTypes[key].active = key === item
    })
    if (!updateScroll) {
      return
    }
    const tabsContent = this.$el.querySelector(
      '.ant-tabs .ant-tabs-left-content'
    ) as HTMLElement
    const chartItemHeight =
      (this.$el.querySelectorAll('.chart-item')[1] as HTMLElement).offsetTop -
      this.DIVIDER_HEIGHT
    const currentGroupPos = this.chartTypePosList.findIndex(
      (chartItem: any) => chartItem.name === item
    )
    const { start } = this.chartTypePosList[currentGroupPos]
    // 每一个divider和当前tab下的第一个chart-item之间没有margin，而当前tab下的chart-item之间都有一个sibling margin
    const breforeAdjust =
      chartItemHeight * start + currentGroupPos * this.DIVIDER_HEIGHT
    const scrollAdjuestedWithSiblingMargin =
      start !== 0
        ? breforeAdjust - this.CHART_ITEM_SIBLING_MARGIN * currentGroupPos
        : breforeAdjust
    tabsContent.scrollTop = scrollAdjuestedWithSiblingMargin
    this.scrollInChartTabTopValue = scrollAdjuestedWithSiblingMargin
  }

  /**
   * 删除我的组件 中添加的widget
   */
  async onDelete(widget: Widget) {
    const responseData = await this.deleteWidget(widget)
    if (responseData.code === 100) {
      this.myComponentLoadedFlag = false
      //  init para
      this.myComponentWidget = {
        dataset: [],
        pipeline: [],
        gis: [],
      }
      this.myComponentWidgetList = []
      this.onTabChange('myComponent')
      this.$message.success('删除可视化成功')
    } else {
      this.$message.error('删除可视化失败')
    }
  }
  /**
   * 删除widget
   */
  async deleteWidget(widget: Widget) {
    const parameters: any = {
      id: widget.id,
      projectId: localStorage.getItem('projectId'),
      type: 'task',
    }
    const response = await deleteWidget({
      data: parameters,
    })
    return response.data
  }

  /**
   * 保存/更新 可视化
   */
  async onSave(widget: Widget) {
    const responseData =
      this.myModalVisualizationConfig.widgetId === undefined
        ? await this.saveWidget(widget)
        : await this.updateWidget(widget)
    if (responseData.code === 100) {
      this.myComponentLoadedFlag = false
      //  init para
      this.myComponentWidget = {
        dataset: [],
        pipeline: [],
        gis: [],
      }
      this.myComponentWidgetList = []
      this.myModalVisualizationConfig = {
        visible: false,
        chartConfig: {},
        datasetList: {
          dataset: [],
          pipeline: [],
        },
        widgetId: undefined,
      }
      this.onTabChange('myComponent')
      this.$message.success('保存可视化成功')
    } else {
      this.$message.error('保存可视化失败')
    }
  }
  /**
   * 保存widget
   */
  async saveWidget(widget: Widget) {
    const parameters = {
      data: {
        dashboardId: -1,
        data: widget.data,
        name: widget.name,
        randomId: Math.floor(Math.random() * 10000),
        tid: widget.tid,
        type: widget.type,
        projectId: localStorage.getItem('projectId'),
      },
    }
    const response = await saveWidget(parameters)
    return response.data
  }
  /**
   * 更新widget
   */
  async updateWidget(widget: Widget) {
    const parameters = {
      data: {
        dashboardId: -1,
        data: widget.data,
        id: this.myModalVisualizationConfig.widgetId,
        name: widget.name,
        type: widget.type,
        projectId: localStorage.getItem('projectId'),
      },
    }
    const response = await updateWidget(parameters)
    return response.data
  }

  // 我的组件mouseenter事件，保存当前hover的组件wigetId到store，实现交叉高亮
  doCrossHighlighting(widget: any) {
    const { id: widgetId, tid, chartType } = widget
    // 表格没有widgetId，只能用tid来区分
    const hoverId = chartType[1] === 'table' ? tid : widgetId
    VisualizationStore.setHoverMyComponentId(hoverId)
  }
  // 我的组件mouseleave事件，重置store中当前hover的组件Id，取消交叉高亮
  cancelCrossHighlighting() {
    VisualizationStore.setHoverMyComponentIdDefault()
  }
  /**
   * 我的组件 chart 鼠标down事件
   */
  onMyMouseDown(event: any) {
    this.mouseDownPos.x = event.layerX - 6
    this.mouseDownPos.y = event.layerY
    // 这里用来把offset 先存储起来，方便drop时定位
    VisualizationStore.setDraggingOffset(this.mouseDownPos)
  }
  /**
   * 我的组件 - 拖拽组件
   */
  onMyDragstart(
    event: any,
    widget: any,
    widgetMenuKey: 'dataset' | 'pipeline'
  ) {
    if (this.isVisitor || this.lockMode) {
      return
    }
    const { chartType = [] } = widget
    event.dataTransfer.setData(
      'text',
      `${widget.id}$${widget.tid}$${
        widget.type
      }$${widgetMenuKey}$${chartType.join('|')}`
    )
  }
  /**
   * 解析我的组件 widgets
   */
  parseMyComponentWidget(widgetResults: any) {
    ;['dataset', 'pipeline', 'gis'].forEach((keyName, keyIndex) => {
      // @ts-ignore
      this.myComponentWidget[keyName] = widgetResults[keyIndex].taskWidgets
        .map((task: any) => {
          return {
            ...task,
            widgets: task.widgets.map((widget: any) => {
              if (
                !this.myComponentWidgetList.some((w: any) => {
                  return (
                    w.id === widget.id &&
                    w.tid === widget.tid &&
                    w.type === widget.type
                  )
                })
              ) {
                this.myComponentWidgetList.push({
                  ...widget,
                  pipelineId: widgetResults[keyIndex].pipelineId,
                })
              }

              let chartType: Array<string> =
                keyName === 'gis' ? ['gis', 'gisView'] : ['table', 'table']
              if (widget.data?.chartType) {
                chartType = Array.isArray(widget.data.chartType)
                  ? [...widget.data.chartType]
                  : [widget.data.chartType, widget.data.chartType]
              } else if (widget.type === 'configuration') {
                chartType = ['configuration', 'configuration']
              }
              const imgSource = this.chartMaps[chartType[0]]?.filter(
                (chartConfig: any) => chartConfig.name === chartType[1]
              )[0]?.imgSrc
              return {
                ...widget,
                chartType,
                imgSrc: keyName === 'gis' ? widget.data?.thumbnail : imgSource,
                taskName: task.taskName,
              }
            }),
          }
        })
        .filter((task: any) => {
          return (
            task.widgets.length > 0 &&
            !(
              keyName === 'pipeline' &&
              task.widgets.find((widget: any) => widget.id === null)?.type ===
                null
            )
          )
        })
    })

    // DataViewStore.pipelineNodes中存储的有数据格式化信息
    // 在这里，将数据格式化信息存储到组件的配置信息中
    this.myComponentWidgetList?.forEach((widget: any) => {
      const node = DataViewStore.pipelineNodes?.find((pipelineNode: any) => {
        return widget.tid === pipelineNode.id
      })
      widget.numberFormat = node?.data?.output[0]?.numberFormat
    })
    VisualizationStore.updatePipelineWidgetList(this.myComponentWidgetList)
  }

  /**
   * 我的组件-搜索关键词
   */
  onMySearchWordsChange() {
    const keywords = this.myComponentSearchWords.trim()
    if (!keywords) {
      // 还原无SearchWords时的展开状态
      this.myComponentCollapseActiveKeys =
        this.myComponentCollapseActiveKeysCache
      this.myComponentCollapseActiveKeysCache = null
      return
    }
    if (this.myComponentCollapseActiveKeysCache === null) {
      // 保存无SearchWords时的展开状态
      this.myComponentCollapseActiveKeysCache =
        this.myComponentCollapseActiveKeys
      this.myComponentCollapseActiveKeys = {
        total: ['dataset', 'pipeline'],
        dataset: [],
        pipeline: [],
      }
    }
    const result: { dataset: Array<any>; pipeline: Array<any> } = {
      dataset: [],
      pipeline: [],
    }
    Object.keys(result).forEach((typeKey: string) => {
      // @ts-ignore
      this.myComponentWidget[typeKey].forEach((taskWidgets: any) => {
        if (taskWidgets.taskName.includes(keywords)) {
          // pipeline名字包含关键字，则pipeline下全部图表都加入
          // @ts-ignore
          result[typeKey].push(taskWidgets)
        } else {
          const widgets = taskWidgets.widgets.filter((widget: any) =>
            widget.name?.includes(keywords)
          )
          if (widgets.length > 0) {
            // @ts-ignore
            result[typeKey].push({
              ...taskWidgets,
              widgets,
            })
          }
        }
      })
    })
    this.myComponentWidgetSearchResult = result
  }

  onMyCollapseChange() {
    ;['dataset', 'pipeline']
      .filter((k) => !this.myComponentCollapseActiveKeys.total.includes(k))
      .forEach((k) => {
        this.myComponentCollapseActiveKeys[k] = []
      })
  }
  /**
   * 切换 我的组件，系统组件
   */
  onTabChange(activeKey: string) {
    //  如果切换到我的组件，并且是首次加载
    if (activeKey === 'myComponent' && !this.myComponentLoadedFlag) {
      this.myComponentLoadedFlag = true
      const projectId = this.$route.params.id
      widgetQueryByProject({
        data: {
          projectId,
        },
      })
        .then((response) => {
          this.parseMyComponentWidget(response.data.result)
        })
        .catch(() => {
          this.myComponentLoadedFlag = false
        })
    }
    if (activeKey === 'systemComponent') {
      const tabsContent = this.$el.querySelector(
        '.ant-tabs .ant-tabs-left-content'
      ) as HTMLElement
      tabsContent.scrollTop = this.scrollInChartTabTopValue
    }
  }

  /**
   * 添加我的可视化
   */
  addMyVisualization(taskWidget: any, widgetType: 'dataset' | 'pipeline') {
    // 存储当前操作的widget，以便判断折线图是否有“动态展示”选项
    VisualizationStore.setCurrentTaskWidget(taskWidget)
    console.log(VisualizationStore?.currentTaskWidget?.hasTempResult)
    const widget = taskWidget.widgets.find((w: any) => {
      return w.id === null
    })
    if (widget) {
      const datasetList: any = {
        dataset: [],
        pipeline: [],
      }
      datasetList[widgetType].push({
        id: widget.tid,
        name: widget.taskName,
        tableName: widget.data.table,
      })
      this.myModalVisualizationConfig = {
        chartConfig: {
          widgetType,
          chartOptions: {
            dataId: widget.tid,
            dataType: widget.type === 'configuration' ? 'task' : widget.type,
          },
        },
        datasetList,
        visible: true,
        widgetId: undefined,
      }
    }
  }
  /**
   * 编辑我的可视化
   */
  editMyVisualization(widget: any, widgetType: 'dataset' | 'pipeline' | 'gis') {
    if (widgetType === 'gis') {
      setLocalStorage('currentTabId', String(widget.tid))
      this.$router.push(`/project/${this.$route.params.id}/gis`)
      return
    }
    const datasetList: any = {
      dataset: [],
      pipeline: [],
    }
    datasetList[widgetType].push({
      id: widget.tid,
      name: widget.taskName,
      tableName: widget.data.table,
    })
    this.myModalVisualizationConfig = {
      chartConfig: {
        ...widget.data,
        widgetJson: {
          ...widget.data.widgetJson,
        },
        widgetType,
      },
      datasetList,
      visible: true,
      widgetId: widget.id,
    }
  }
}
</script>
<style lang="less" scoped>
.comp-side-graph-panel {
  background: #fff;
  border-right: 1px solid #e9e9e9;
  height: 100%;
  user-select: none;
  width: 230px;

  /deep/ .ant-tabs-tabpane-active {
    height: 100%;
  }

  /deep/ .ant-tabs-tabpane-inactive {
    height: 0;
  }

  /deep/ .ant-tabs.comp-side-graph-panel-tabs {
    height: 100%;

    > .ant-tabs-bar .ant-tabs-nav {
      height: 39px;
      letter-spacing: 1px;

      // tab标题
      .ant-tabs-tab {
        color: #222432;
        font-weight: 500;
        opacity: 0.5;

        &.ant-tabs-tab-active {
          color: #222432;
          font-weight: 600;
          opacity: 1;
        }
      }
    }

    .ant-tabs-content {
      height: 100%;
    }

    .spin-box {
      height: auto;
      width: 220px;
    }

    .spin {
      display: block;
      margin: 48px auto;
    }
  }

  /deep/ .ant-tabs-top-bar {
    margin: 0;
  }

  .search-box {
    box-sizing: border-box;
    padding: 10px;
    width: 100%;

    /deep/ .ant-input {
      border-radius: 16px;
    }
  }

  .tab-box {
    background: #f9f9fc;
    height: calc(100% - 95px);
    transition: all ease 0.2s;
    width: 100%;

    /deep/ .ant-tabs {
      height: 100%; // 设置height会有回弹动画
      // min-height: 100%;

      .ant-tabs-tab span {
        font-size: 24px;
        margin-left: -11px;
      }
    }

    /deep/ .ant-tabs-nav-wrap {
      background: #fff;
      height: 100%;
    }

    /deep/ .ant-tabs-tab {
      border: 1px solid transparent;
      margin: 0;
      width: 40px;

      &.ant-tabs-tab-active {
        background: #f0f3ff !important;
      }

      &:hover {
        background-color: #f9f9fc;
      }
    }

    /deep/ .ant-tabs .ant-tabs-left-bar {
      .ant-tabs-ink-bar {
        display: none !important;
      }
    }

    /deep/ .ant-tabs .ant-tabs-left-content {
      height: 100%;
      overflow-x: hidden;
      overflow-y: scroll;
      padding-left: 0;

      &::-webkit-scrollbar {
        height: 6px;
        visibility: hidden;
        width: 5px;
      }

      &::-webkit-scrollbar-thumb {
        background: rgba(144, 147, 153, 0.5);
        border-radius: 6px;
        visibility: hidden;
      }

      &:hover {
        &::-webkit-scrollbar {
          visibility: visible;
        }

        &::-webkit-scrollbar-thumb {
          visibility: visible;
        }
      }

      &::-webkit-scrollbar-track {
        background: transparent;
        border-radius: 5px;
      }
    }

    /deep/ .ant-card {
      border-width: 0;
      text-align: left;

      .ant-card-body {
        padding: 0;
      }

      .ant-card-head {
        background-color: #f6f8ff;
        border-bottom-width: 0;
      }
    }
  }

  .chart-item {
    background: #fff;
    border: 1px solid #e9e9eb;
    border-radius: 4px;
    cursor: inherit;
    height: 122px;
    line-height: 108px;
    margin-left: 5px;
    text-align: center;
    width: 170px;

    &:hover {
      border: 1px solid #dfe1fd;
    }
  }

  .divider {
    font-size: 15px;
    font-weight: 600;
    line-height: 40px;
    pointer-events: none;
    text-align: center;
    width: 100%;
  }

  .chart-item-search {
    background: #fff;
    border: 1px solid #e9e9eb;
    border-radius: 4px;
    cursor: pointer;
    height: 142px;
    line-height: 108px;
    margin-bottom: 5px;
    margin-left: 5px;
    text-align: center;
    width: calc(100% - 10px);

    &:hover {
      border: 1px solid #dfe1fd;
    }
  }

  .chart-item + .chart-item {
    margin-top: 10px;
  }

  .my-component {
    overflow-y: auto;
    padding-bottom: 30px;

    &::-webkit-scrollbar {
      height: 6px;
      visibility: hidden;
      width: 6px;
    }

    &::-webkit-scrollbar-thumb {
      background: rgba(144, 147, 153, 0.5);
      border-radius: 6px;
      visibility: hidden;
    }

    &:hover {
      &::-webkit-scrollbar {
        visibility: visible;
      }

      &::-webkit-scrollbar-thumb {
        visibility: visible;
      }
    }

    &::-webkit-scrollbar-track {
      background: transparent;
      border-radius: 5px;
    }

    /deep/ .ant-collapse-content-box {
      padding: 0;
    }

    /deep/ .ant-collapse > .ant-collapse-item,
    /deep/ .ant-collapse-content {
      border: 0;
    }

    /deep/ .ant-collapse {
      background-color: #fff;
      border: 0;
      margin-top: 10px;

      &.ant-collapse-icon-position-left .ant-collapse-header {
        padding-left: 30px;

        .ant-collapse-arrow {
          font-size: 8px;
        }
      }

      .widget-menu-item-div {
        .ant-collapse {
          margin-top: 0;
        }

        .ant-collapse-header {
          margin-left: 20px;
        }

        .widget-item-list-div {
          background-color: #f9f9fc;
          position: relative;

          .ant-list {
            max-height: 212px;
            overflow-y: auto;
            padding: 10px 10px 28px;

            &::-webkit-scrollbar {
              width: 2px;
            }

            &::-webkit-scrollbar-thumb {
              // background: #c7c9cc;
              background: transparent;
              border-radius: 2px;
            }

            &:hover::-webkit-scrollbar-thumb {
              background: #c7c9cc;
            }
          }

          .widget-item-list-add-btn {
            background: #eff0fe;
            border-width: 0;
            bottom: 0;
            height: 28px;
            left: 0;
            line-height: 28px;
            position: absolute;
            right: 0;
            text-align: center;

            i {
              color: #222432;
              opacity: 0.5;
            }

            span {
              color: #222432;
              font-size: 12px;
              font-weight: 400;
              letter-spacing: 1.12px;
              opacity: 0.5;
            }

            &:hover {
              cursor: pointer;

              span,
              i {
                color: #6973ff;
                opacity: 1;
              }
            }
          }
        }

        .widget-item-div {
          border-radius: 2px;
          box-shadow: 0 0 4px 0 rgba(88, 98, 230, 0.2);
        }

        .ant-list-item {
          background-color: #fff;
          margin-bottom: 10px;
        }
      }
    }

    /deep/ .ant-collapse-header {
      padding: 9px 16px;
    }

    .empty-info {
      height: 40px;
      line-height: 40px;
      text-align: center;
    }

    .chart-box {
      border: 1px solid #e9e9eb;
      border-radius: 4px;
      cursor: pointer;
      height: 220px;
      margin: 10px auto 0;
      overflow: hidden;
      position: relative;
      width: 210px;

      .chart-title {
        background-color: #f9faff;
        color: rgb(93, 99, 126);
        font-size: 14px;
        font-weight: 600;
        height: 30px;
        line-height: 30px;
        padding: 0 12px;

        .delete-button {
          float: right;
        }
      }

      .chart-box-real {
        height: 220px !important;
        pointer-events: none;
        position: relative;
        width: 208px !important;
      }

      .mask {
        background: transparent;
        bottom: 0;
        cursor: pointer;
        height: calc(100% - 30px);
        left: 0;
        position: absolute;
        width: 100%;
        z-index: 310;
      }
    }

    .my-component-list-title {
      color: #5d637e;
      font-weight: 400;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      width: 100%;

      &.my-component-list-header-title {
        font-weight: 600;
      }
    }

    .hover-visible {
      background-color: rgba(127, 127, 127, 0.5);
      bottom: 0;
      left: 0;
      position: absolute;
      right: 0;
      top: 0;
      visibility: hidden;
      z-index: 9;

      .hover-content {
        color: rgb(255, 255, 255);
        margin-top: -14px;
        overflow: hidden;
        padding: 5px;
        position: relative;
        text-align: center;
        text-overflow: ellipsis;
        top: 50%;
        white-space: nowrap;
      }

      .delete-icon {
        color: #fff;
        font-size: 13px;
        position: absolute;
        right: 3px;
        top: 0.5px;
      }

      .edit-icon {
        color: #fff;
        font-size: 14px;
        position: absolute;
        right: 20px;
        top: 0;
      }
    }

    .widget-item-div:hover > .hover-visible {
      visibility: visible;
    }
  }

  .icon-map {
    transform: scale(1.2);
  }

  &.guest-mode {
    /deep/ .ant-collapse .widget-menu-item-div .widget-item-list-div .ant-list {
      padding-bottom: 0;
    }
  }
}

.chart-item-container-search {
  height: 100%;
  overflow-x: hidden;
  overflow-y: scroll;

  &::-webkit-scrollbar {
    height: 6px;
    visibility: hidden;
    width: 6px;
  }

  &::-webkit-scrollbar-thumb {
    background: rgba(144, 147, 153, 0.5);
    border-radius: 6px;
    visibility: hidden;
  }

  &:hover {
    &::-webkit-scrollbar {
      visibility: visible;
    }

    &::-webkit-scrollbar-thumb {
      visibility: visible;
    }
  }

  &::-webkit-scrollbar-track {
    background: transparent;
    border-radius: 5px;
  }
}

.chart-item-container {
  &::after {
    content: '';
    display: block;
    height: var(--after-height);
  }
}

.bottom-spinning {
  width: 100%;
}
</style>
